package se.tinkerway.bomberman.domain;

import se.tinkerway.bomberman.domain.tile.Bomb;
import se.tinkerway.bomberman.domain.tile.Flame;
import se.tinkerway.bomberman.view.Frame;

public class TileMap {
	private Tile[][] tiles;
	private double maxX;
	private double maxY;
	private int width;
	private int height;
	
	private static Tile EMPTY = new Tile() {
		@Override
		public boolean burn(TileCallback callback) {
			return true;
		}

		@Override
		public Frame getFrame() {
			return null;
		}

		@Override
		public boolean isPassable() {
			return true;
		}

		@Override
		public void onEnter(Bomberman bomberman) {
		}

		@Override
		public void timeTick(long deltaTime, TileCallback callback) {
		}
	};

	public TileMap(Tile[][] tiles) {
		this.tiles = tiles;
		this.height = tiles.length;
		this.width = tiles[0].length;
		for (int indx=1; indx<tiles.length; indx++) {
			if (tiles[indx].length != width) {
				throw new IllegalArgumentException("All rows must have same width!");
			}
		}
		for (int i=0; i<tiles.length; i++) {
			for (int j=0; j<tiles[i].length; j++) {
				if (tiles[i][j] == null) {
					tiles[i][j] = EMPTY;
				}
			}
		}
		this.maxX = width;
		this.maxY = tiles.length;
	}
	
	public void timeTick(long deltaTime) {
		for (int i=0; i<tiles.length; i++) {
			for (int j=0; j<tiles[i].length; j++) {
				timeTickAt(deltaTime, i, j);
			}
		}
	}
	
	private void timeTickAt(long deltaTime, final int y, final int x) {
		tiles[y][x].timeTick(deltaTime, new TileCallback() {
			@Override
			public void explode(int size) {
				tiles[y][x] = new Flame();
				addFlames(y, x, 1, 0, size);
				addFlames(y, x, -1, 0, size);
				addFlames(y, x, 0, 1, size);
				addFlames(y, x, 0, -1, size);
			}

			private void addFlames(int y, int x, int ydelta, int xdelta, int size) {
				for (int indx=1; indx<size; indx++) {
					y += ydelta;
					x += xdelta;
					if (y < 0 || y > maxY || x < 0 || x > maxX) {
						break;
					}
					if (tiles[y][x].burn(this) == false) {
						break;
					}
					System.out.println("FLAME: x=" + x + " y=" + y);
					tiles[y][x] = new Flame();
				}
			}

			@Override
			public void remove() {
				tiles[y][x] = EMPTY;
			}

			@Override
			public void spawn(Tile tile) {
				tiles[y][x] = tile;
			}
		});
	}

	public Position move(Bomberman bomberman, Position delta) {
		Position position = bomberman.getPosition();
		while (true) {
			if (delta.x != 0) {
				double dist;
				if (Math.abs(delta.x) > Bomberman.SIZE) {
					dist = Math.signum(delta.x)*Bomberman.SIZE;
				} else {
					dist = delta.x;
				}
				position = doMove(position, new Position(dist, 0));
				delta.x -= dist;
			} else if (delta.y != 0) {
				double dist;
				if (Math.abs(delta.y) > Bomberman.SIZE) {
					dist = Math.signum(delta.y)*Bomberman.SIZE;
				} else {
					dist = delta.y;
				}
				position = doMove(position, new Position(0, dist));
				delta.y -= dist;
			} else {
				break;
			}
		}
		return position;
	}

	private Position doMove(Position pos, Position delta) {
		int x1 = (int)(pos.x);
		int y1 = (int)(pos.y);
		addDeltaToPosition(pos, delta);
		int x2 = Math.min(width-1, Math.max(0, (int)(pos.x+Math.signum(delta.x)*Bomberman.SIZE)));
		int y2 = Math.min(height-1, Math.max(0, (int)(pos.y+Math.signum(delta.y)*Bomberman.SIZE)));
		if (x1 != x2 || y1 != y2) {
			if (tiles[y2][x2].isPassable() == false) {
				if (x1 > x2) {
					pos.x = x2+1+Bomberman.SIZE;
				} else if (x1 < x2) {
					pos.x = x1+Bomberman.SIZE;
				}
				if (y1 > y2) {
					pos.y = y2+1+Bomberman.SIZE;
				} else if (y1 < y2) {
					pos.y = y1+Bomberman.SIZE;
				}
			}
		}
		return pos;
	}

	private void addDeltaToPosition(Position pos, Position delta) {
		pos.x += delta.x;
		pos.y += delta.y;
		if (pos.x < Bomberman.SIZE) {
			pos.x = Bomberman.SIZE;
		}
		if (pos.y < Bomberman.SIZE) {
			pos.y = Bomberman.SIZE;
		}
		if (pos.x > maxX-Bomberman.SIZE) {
			pos.x = maxX-Bomberman.SIZE;
		}
		if (pos.y > maxY-Bomberman.SIZE) {
			pos.y = maxY-Bomberman.SIZE;
		}
	}

	public boolean swawnIfEmpty(Bomb bomb, Position position) {
		Tile tile = tiles[(int) position.y][(int) position.x];
		
		if (tile == EMPTY) {
			tiles[(int) position.y][(int) position.x] = bomb;
			
			return true;
		} else {
			return false;
		}
	}

	public int getSize() {
		return tiles.length;
	}
	
	public Tile[][] getTiles() {
		return tiles;
	}
}


